package nl.unimaas.group11.project1_3.graphics;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GraphicsConfiguration;
import java.util.ArrayList;
import java.util.Enumeration;

import javax.media.j3d.AmbientLight;
import javax.media.j3d.Appearance;
import javax.media.j3d.Background;
import javax.media.j3d.BoundingSphere;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.Canvas3D;
import javax.media.j3d.DirectionalLight;
import javax.media.j3d.Group;
import javax.media.j3d.Material;
import javax.media.j3d.Node;
import javax.media.j3d.SceneGraphObject;
import javax.media.j3d.Shape3D;
import javax.media.j3d.Transform3D;
import javax.media.j3d.TransformGroup;
import javax.media.j3d.TransparencyAttributes;
import javax.swing.JPanel;
import javax.vecmath.Color3f;
import javax.vecmath.Point3d;
import javax.vecmath.Vector3d;
import javax.vecmath.Vector3f;

import nl.unimaas.group11.project1_3.algo.DataProcessor;

import com.sun.j3d.loaders.Scene;
import com.sun.j3d.loaders.objectfile.ObjectFile;
import com.sun.j3d.utils.behaviors.vp.OrbitBehavior;
import com.sun.j3d.utils.geometry.Box;
import com.sun.j3d.utils.universe.SimpleUniverse;
import com.sun.j3d.utils.universe.ViewingPlatform;

public class WorldViewer extends JPanel {

	private static final long serialVersionUID = 1L;

	private static final int WIDTH = 900;
	private static final int HEIGHT = 600;

	private static final int BOUNDSIZE = 100;

	private static final Point3d INITIALVIEWPOSITION = new Point3d(2, 2, 1);

	private static final Color3f WHITE = new Color3f(1.0f, 1.0f, 1.0f);
	private static final Color3f BACKGROUNDCOLOR = new Color3f(0.22f, 0.22f, 0.22f);

	private static final Vector3f LIGHTDIRECTION1 = new Vector3f(-1.0f, -1.0f, -1.0f);
	private static final Vector3f LIGHTDIRECTION2 = new Vector3f(1.0f, -1.0f, 1.0f);

	public TransformGroup truckTransformGroup;

	public SimpleUniverse universe;
	public BranchGroup contents, truckBG, cubeBG;
	private BoundingSphere bounds;

	private Appearance cubeRed, cubeBlue, cubeGreen;

	private Appearance app;
	private Shape3D setStuff;

	public ArrayList<String> shapeTruckList = new ArrayList<String>();;

	public WorldViewer() {

		setLayout(new BorderLayout());
		setOpaque(true);
		setPreferredSize(new Dimension(WIDTH, HEIGHT));

		app = new Appearance();
		app.setCapability(Appearance.ALLOW_COLORING_ATTRIBUTES_READ);
		app.setCapability(Appearance.ALLOW_COLORING_ATTRIBUTES_WRITE);
		app.setCapability(Appearance.ALLOW_MATERIAL_READ);
		app.setCapability(Appearance.ALLOW_MATERIAL_WRITE);
		app.setCapability(Appearance.ALLOW_RENDERING_ATTRIBUTES_WRITE);

		setStuff = new Shape3D();
		setStuff.setCapability(Shape3D.ALLOW_APPEARANCE_OVERRIDE_READ);
		setStuff.setCapability(Shape3D.ALLOW_APPEARANCE_OVERRIDE_WRITE);
		setStuff.setCapability(Shape3D.ALLOW_APPEARANCE_READ);
		setStuff.setCapability(Shape3D.ALLOW_APPEARANCE_WRITE);
		setStuff.setCapability(Shape3D.ALLOW_PICKABLE_READ);
		setStuff.setCapability(Shape3D.ALLOW_PICKABLE_WRITE);

		GraphicsConfiguration config = SimpleUniverse.getPreferredConfiguration();

		Canvas3D canvas = new Canvas3D(config);
		add("Center", canvas);
		canvas.setFocusable(true);
		canvas.requestFocus();

		universe = new SimpleUniverse(canvas);
		createSceneGraph();
		initialPosition();
		cameraControl(canvas);

		universe.addBranchGraph(contents);
		

	}

	private void createSceneGraph() {

		// this initialises the scene
		contents = new BranchGroup();
		contents.setCapability(BranchGroup.ALLOW_DETACH);
		contents.setCapability(BranchGroup.ALLOW_CHILDREN_WRITE);
		contents.setCapability(BranchGroup.ALLOW_PICKABLE_WRITE);
		contents.setCapability(BranchGroup.ALLOW_CHILDREN_EXTEND);
		contents.setCapability(BranchGroup.ALLOW_PARENT_READ);

		setStuff.setCapability(Shape3D.ALLOW_APPEARANCE_OVERRIDE_READ);
		setStuff.setCapability(Shape3D.ALLOW_APPEARANCE_OVERRIDE_WRITE);
		setStuff.setCapability(Shape3D.ALLOW_APPEARANCE_READ);
		setStuff.setCapability(Shape3D.ALLOW_APPEARANCE_WRITE);
		setStuff.setCapability(Shape3D.ALLOW_PICKABLE_READ);
		setStuff.setCapability(Shape3D.ALLOW_PICKABLE_WRITE);

		bounds = new BoundingSphere(new Point3d(0, 0, 0), BOUNDSIZE);

		// this is where the objects are added. you can (should) call it with a
		// method e.g. addTruck()

		background();
		addTruck(1);
		lighting();
		cubeRed = createAppearance(Color.RED);
		cubeBlue = createAppearance(Color.BLUE);
		cubeGreen = createAppearance(Color.GREEN);
		
		
	}

	private Appearance createAppearance(Color color) {
		Appearance cubeColor = new Appearance();
		Color3f ambientColour = new Color3f();
		ambientColour.set(color);
		Color3f emissiveColour = new Color3f(0.0f, 0.0f, 0.0f);
		Color3f specularColour = new Color3f(1.0f, 1.0f, 1.0f);
		Color3f diffuseColour = new Color3f();
		diffuseColour.set(color);

		float shininess = 50.0f;
		cubeColor.setMaterial(new Material(ambientColour, emissiveColour, diffuseColour, specularColour, shininess));
		return cubeColor;
	}

	public void draw3DSolution(int[][][] solution) {
		cubeBG = new BranchGroup();

		 

		for (int x = 0; x < solution[0][0].length; x++) {
			for (int y = 0; y < solution[0].length; y++) {
				for (int z = 0; z < solution.length; z++) {
					int boxNumber = solution[z][y][x];
					Appearance color;
					if (boxNumber != 0) {
						
					
					
					if (DataProcessor.pents) {
						
						if(boxNumber==1){
							color = cubeRed;
						}else if(boxNumber==2){
							color = cubeBlue;
						}else{
							color = cubeGreen;
						}
						
						
						
					} else {
						if (boxNumber == 1 || boxNumber == 2 || boxNumber == 3) {
							color = cubeRed;
						} else if (boxNumber == 4 || boxNumber == 5 || boxNumber == 6 || boxNumber == 7 || boxNumber == 8 || boxNumber == 9) {
							color = cubeBlue;
						} else {
							color = cubeGreen;
						}
					}

					Box box = new Box(0.025f, 0.025f, 0.025f, color);
					TransformGroup tg = new TransformGroup();
					Transform3D transform = new Transform3D();
					Vector3f vector = new Vector3f((float) ((x / 20.0) - 0.1), (float) ((y / 20.0) - 0.13), (float) ((z / 20.0) - 0.93));
					transform.setTranslation(vector);
					tg.setTransform(transform);
					tg.addChild(box);
					cubeBG.addChild(tg);

				}
				}
			}

		}
		universe.addBranchGraph(cubeBG);
	}

	public void visitNode(Node node)

	{

		if (node instanceof Group) {
			Group g = (Group) node;
			Enumeration<?> enumKids = g.getAllChildren();
			while (enumKids.hasMoreElements()) { // visit children
				SceneGraphObject obj = (SceneGraphObject) enumKids.nextElement();
				if (obj instanceof Node)
					visitNode((Node) obj);
			}
		} else if (node instanceof Shape3D) {

			setStuff = (Shape3D) node;
			if (!shapeTruckList.contains(setStuff.toString())) {
				shapeTruckList.add(setStuff.toString());
				


					makeAlmostTransparent(setStuff, 0.7f);

			} 
		}

	}

	

	private void makeAlmostTransparent(Shape3D shape, float transparency)
	// make the shape almost transparent
	{
		Appearance app = shape.getAppearance();
		TransparencyAttributes ta = new TransparencyAttributes();
		ta.setTransparencyMode(TransparencyAttributes.BLENDED);
		ta.setTransparency(transparency);
		app.setTransparencyAttributes(ta);
		shape.setAppearance(app);

	}

	private void lighting() {

		AmbientLight ambientLightNode = new AmbientLight(WHITE);// /!
		ambientLightNode.setInfluencingBounds(bounds);
		contents.addChild(ambientLightNode);

		DirectionalLight light1 = new DirectionalLight(WHITE, LIGHTDIRECTION1);
		DirectionalLight light2 = new DirectionalLight(WHITE, LIGHTDIRECTION2);

		light1.setInfluencingBounds(bounds);
		contents.addChild(light1);

		light2.setInfluencingBounds(bounds);
		contents.addChild(light2);
	}

	private void background() {
		Background background = new Background();
		background.setApplicationBounds(bounds);
		background.setColor(BACKGROUNDCOLOR);
		contents.addChild(background);
	}

	private void cameraControl(Canvas3D c) {
		OrbitBehavior orbit = new OrbitBehavior(c, OrbitBehavior.REVERSE_ALL);
		orbit.setSchedulingBounds(bounds);
		ViewingPlatform vp = universe.getViewingPlatform();
		vp.setViewPlatformBehavior(orbit);
	}

	private void initialPosition() {
		ViewingPlatform vp = universe.getViewingPlatform();
		TransformGroup tg = vp.getViewPlatformTransform();

		Transform3D t3d = new Transform3D();
		tg.getTransform(t3d);

		t3d.lookAt(INITIALVIEWPOSITION, new Point3d(0, 0, 0), new Vector3d(0, 1, 0));
		t3d.invert();

		tg.setTransform(t3d);
	}



	public void addTruck(int material) {

		truckBG = new BranchGroup();
		truckBG.setCapability(BranchGroup.ALLOW_DETACH);
		truckBG.setCapability(BranchGroup.ALLOW_CHILDREN_WRITE);
		truckBG.setCapability(BranchGroup.ALLOW_PICKABLE_WRITE);
		truckBG.setCapability(BranchGroup.ALLOW_CHILDREN_EXTEND);
		truckBG.setCapability(BranchGroup.ALLOW_PARENT_READ);
		// position the truck
		Transform3D t3d = new Transform3D();
		t3d.set(new Vector3f(0, 0, 0));
		truckTransformGroup = new TransformGroup(t3d);
		truckTransformGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
		truckTransformGroup.setCapability(TransformGroup.ALLOW_PICKABLE_WRITE);
		truckTransformGroup.setCapability(TransformGroup.ALLOW_PICKABLE_READ);

		try {

			//
			Scene s = null;
			ObjectFile f = new ObjectFile();
			f.setFlags(ObjectFile.RESIZE | ObjectFile.TRIANGULATE | ObjectFile.STRIPIFY);

			String s1 = "res/models/truck.obj";
			s = f.load(s1);

			BranchGroup testing = s.getSceneGroup();
			testing.setCapability(BranchGroup.ALLOW_PICKABLE_READ);
			// Shape3D testest = (Shape3D) testing;

			visitNode(testing);

			truckTransformGroup.addChild(testing);
		}

		catch (java.io.FileNotFoundException ex) {
			System.out.println("File was not found");
		} catch (Exception e) {
			System.out.println("An error occured");
		}

		truckBG.addChild(truckTransformGroup);
		contents.addChild(truckBG);

	}



}
